#!/usr/bin/env python

class Node:
    def __init__(self, item = None, p = None):
        self._item = item
        self._next = p
    
    @property
    def next(self):
        return self._next

    @next.setter
    def next(self, p):
        self._next = p

    @property
    def item(self):
        return self._item

    @item.setter
    def item(self, val):
        self._item = val

    def __str__(self):
        return str(self.item)   
    
class LinkedList:
    def __init__(self):
        self._root = None
        self._size = 0

    def add(self, item):
        node = Node(item)
        if self._root is None:
            self._root = node
        else:
            node.next = self._root
            self._root = node
        
        self._size += 1
    
    def append(self, item):
        node = Node(item)
        if self._root is None:
            self._root = node
        else:
            cur = self._root
            while cur.next is not None:
                 cur = cur.next
            
            cur.next = node
        
        self._size += 1


    def remove(self, item):
        if self._root is None:
            return False

        cur = self._root
        while cur.next is not None and cur.next.item != item:
            cur = cur.next
        
        if cur.next is not None:
            cur.next = cur.next.next
            self._size -= 1
            return True
        
        return False
        
    def search(self, item):
        cur = self._root

        while cur is not None and cur.item != item:
            cur = cur.next

        return True if cur is not None else False

    def is_empty(self):
        return self._root is None

    def size(self):
        return self._size

    def __len__(self):
        return self._size

    def index(self, item):
        pos = 0
        cur = self._root
        while cur is not None and cur.item != item:
            cur = cur.next
            pos += 1
        
        return pos if cur is not None else -1

    def insert(self, pos, item):
        if pos == 0 and self._root is None:
            self._root = Node(item)
            self._size += 1
            return True

        if self._size < pos:
            return False

        cur = self._root
        while pos > 1:
            cur = cur.next
            pos -= 1
        
        node = Node(item)
        node.next = cur.next
        cur.next = node
        self._size += 1

        return True

    def pop(self, pos = -1):
        if self._root is None:
            return None

        cur = self._root

        pos = (self._size + pos) % self._size

        while cur.next is not None and pos > 1:
            cur = cur.next
            pos -= 1
        
        if cur.next is not None:
            tmp = cur.next
            cur.next = cur.next.next
            
            self._size -= 1
            return tmp.item
        else:
            return None
    
    def peek(self, pos = -1):
        if self._root is None:
            return None

        cur = self._root
        pos = (pos + self._size) % self._size
        while cur.next is not None and pos > 1:
            cur = cur.next
            pos -= 1
        
        return cur.next.item if cur.next is not None else None
    
    @staticmethod
    def create_linked_list(items, reverse = True):
        linked_list = LinkedList()
        if reverse:
            for item in items:
                linked_list.append(item)
        else:
            for item in items:
                linked_list.add(item)
        
        return linked_list
        
    def __str__(self):
        ans = []
        cur = self._root
        while cur is not None:
            ans.append(cur.item)
            cur = cur.next
        
        return str(ans)

    def reverse(self):
        if self._root is None:
            return

        cur = self._root
        prev = None
        while cur is not None:
            cur.next, cur, prev = prev, cur.next, cur

        self._root = prev      
    
    def max(self):
        return self.min_max(True)
    
    def min(self):
        return self.min_max()

    def min_max(self, reverse = False):
        if self._root is None:
            return None
        
        max_item = self._root.item
        node = self._root

        cur = self._root.next
        if reverse:
            while cur is not None:
                if max_item < cur.item:
                    max_item = cur.item
                    node = cur
                
                cur = cur.next
            
        else:
            while cur is not None:
                if max_item > cur.item:
                    max_item = cur.item
                    node = cur
            
                cur = cur.next
        
        return node


if __name__ == "__main__":
    import random
    nums = [random.randint(0, 100) for _ in range(10)]
    print(nums)

    linked_list = LinkedList.create_linked_list(nums)
    print(linked_list)

    linked_list = LinkedList.create_linked_list(nums, False)
    print(linked_list)
    print(linked_list.size())
    print(linked_list.peek())
    print(linked_list.search(nums[0]))
    print(linked_list.index(nums[1]))
    print(linked_list.remove(nums[1]))
    print(linked_list.pop())
    linked_list.reverse()
    print(linked_list)
    print(linked_list.max())
    print(linked_list.min())